template<class Drawer>
inline TMesh<Drawer>::TMesh() :
	_angle(0), _pos(Vector3d(0, 0, 0)), _scale(Vector3d(1, 1, 1)), _axes(Vector3d(0, 1, 0)), _multiTex_ext(false), model(NULL)
{
}

template<class Drawer>
inline TMesh<Drawer>::TMesh(Model *model) :
	_angle(0), _pos(Vector3d(0, 0, 0)), _scale(Vector3d(1, 1, 1)), _axes(Vector3d(0, 1, 0)), _multiTex_ext(false), model(NULL)
{
	if(model) create();
}

template<class Drawer>
inline TMesh<Drawer>::TMesh(const char *filename) :
  _angle(0), _pos(Vector3d(0, 0, 0)), _scale(Vector3d(1, 1, 1)), _axes(Vector3d(0, 1, 0)), _multiTex_ext(false), model(NULL)

{
    load(filename);
}

template<class Drawer>
inline TMesh<Drawer>::~TMesh()
{
	clear();
}

template<class Drawer>
inline void TMesh<Drawer>::load(Model *model)
{
	clear();
	model = model;
    if(model) create();
}

template<class Drawer>
inline void TMesh<Drawer>::load(const char *filename)
{
	clear();
	model = MediaManager::instance()->getMedia<Model>(filename);
    if(model) create();
}

template<class Drawer>
void TMesh<Drawer>::genNormals()
{
	if(!model)
		return;
	float *vertices = model->getVertices(), *normals = model->getNormals();
	unsigned int *indices = model->getIndices();
	unsigned int nbVertices = model->getVBSize(), nbIndices = model->getIBSize();
	if(!vertices || !indices)
		return;
	if(normals)
		delete[] normals;
	normals = new float[nbVertices];

	float v1[3], v2[3], v3[3], out[3];
	unsigned int aaux;
	for(unsigned int i=0 ; i<nbVertices; i++)
		normals[i] = 0;
	for(unsigned int i=0 ; i<nbIndices; i+=3)
    {
		aaux = indices[i];
		v1[0] = vertices[aaux*3];
		v1[1] = vertices[aaux*3+1];
		v1[2] = vertices[aaux*3+2];
		
		aaux = indices[i+1];
		v2[0] = vertices[aaux*3];
		v2[1] = vertices[aaux*3+1];
		v2[2] = vertices[aaux*3+2];

		aaux = indices[i+2];
		v3[0] = vertices[aaux*3];
		v3[1] = vertices[aaux*3+1];
		v3[2] = vertices[aaux*3+2];

		createnormal(v1, v2, v3, out) ;
		addnormal((normals+*(indices+i)*3),out);
		addnormal((normals+*(indices+i+1)*3),out);
		addnormal((normals+*(indices+i+2)*3),out);
    }
    model->setNormals(normals, nbVertices);
    Drawer::addBuffer(GL_NORMAL_ARRAY, normals, nbVertices*sizeof(float), 3);
}

template<class Drawer>
void TMesh<Drawer>::drawNormals()
{
	float *vertices = model->getVertices(), *normals = model->getNormals();
	unsigned int VBSize = model->getVBSize();
	if(!normals || !vertices)
		return;
		
	Renderer *rdr = Renderer::instance();
    rdr->pushMatrix();
    rdr->scale(_scale);
    rdr->translate(_pos);
    rdr->rotate(_angle, _axes);
	glColor3ub(255, 0, 0);
	glBegin(GL_LINES);
	for(unsigned int i=0; i<VBSize; i+=3)
	{
		glVertex3d(vertices[i], vertices[i+1], vertices[i+2]);
		glVertex3d(vertices[i]+normals[i], vertices[i+1]+normals[i+1], vertices[i+2]+normals[i+2]);
	}
	glEnd();
	glColor3ub(255, 255, 255);
	rdr->popMatrix();
}


template<class Drawer>
inline void TMesh<Drawer>::setTexture(Texture *texture)
{
	if(texture) _textures.push_back(texture);
}

template<class Drawer>
inline void TMesh<Drawer>::setTexture(const char *filename)
{
    Texture *tex = MediaManager::instance()->getMedia<Texture>(filename);
    if(tex) _textures.push_back(tex);
}

template<class Drawer>
inline void TMesh<Drawer>::setPosition(const Vector3d &pos)
{
    _pos = pos;
}

template<class Drawer>
inline void TMesh<Drawer>::setScale(const Vector3d &scale)
{
    _scale = scale;
}

template<class Drawer>
inline void TMesh<Drawer>::setRotation(double angle, const Vector3d &axes)
{
	_angle = angle;
	_axes = axes;
}

template<class Drawer>
inline const Vector3d &TMesh<Drawer>::getPosition()
{
    return _pos;
}

template<class Drawer>
inline const Vector3d &TMesh<Drawer>::getScale()
{
    return _scale;
}

template<class Drawer>
inline void TMesh<Drawer>::draw()
{
    Renderer *rdr = Renderer::instance();

    rdr->pushMatrix();
	
	int vsize = _textures.size();
	if(vsize > 0)
	{
		if(_multiTex_ext)
		{
			for(int i=0; i<vsize; ++i)
				rdr->setMultiTexture(_textures[i], i);
		}
		else
			rdr->setTexture(_textures[0]);
	}

    rdr->scale(_scale);
    rdr->translate(_pos);
    rdr->rotate(_angle, _axes);

    Drawer::draw(model->getIBSize());

	if(vsize > 0)
	{
		if(_multiTex_ext)
		{
			for(int i=0; i<vsize; ++i)
				rdr->setMultiTexture(NULL, i);
		}
		else
			rdr->setTexture(NULL);
	}

    rdr->popMatrix();
}

template<class Drawer>
inline void TMesh<Drawer>::clear()
{
	if(model) model->release();
		model = NULL;
	
	TexIterator it, itEnd = _textures.end();
	for(it = _textures.begin(); it != itEnd; ++it)
	{
		(*it)->release();
		it = _textures.erase(it);
	}
}

template<class Drawer>
inline void TMesh<Drawer>::create()
{
    if(Renderer::instance()->checkExtension("GL_ARB_multitexture")) _multiTex_ext = true;
    else _multiTex_ext = false;

    Drawer::initialize(GL_TRIANGLES);
	Drawer::addBuffer(GL_VERTEX_ARRAY, model->getVertices(), model->getVBSize()*sizeof(float), 3);
    Drawer::addBuffer(GL_TEXTURE_COORD_ARRAY, model->getTexCoords(), model->getTBSize()*sizeof(float), 2);
    Drawer::addBuffer(GL_NORMAL_ARRAY, model->getNormals(), model->getNBSize()*sizeof(float), 3);
    Drawer::addBuffer(GL_INDEX_ARRAY, model->getIndices(), model->getIBSize()*sizeof(unsigned int), 1);
}

template<class Drawer>
void TMesh<Drawer>::normcrossprod(float v1[3], float v2[3], float out[3]) 
{ 
	out[0] = v1[1]*v2[2] - v1[2]*v2[1]; 
	out[1] = v1[2]*v2[0] - v1[0]*v2[2]; 
	out[2] = v1[0]*v2[1] - v1[1]*v2[0]; 
	normalize(out); 
} 

template<class Drawer>
void TMesh<Drawer>::createnormal(float *v1, float *v2, float *v3,float *out) 
{ 
	GLfloat d1[3], d2[3], norm[3]; 
	for (int j = 0; j < 3; j++)
    {
		d1[j] = v2[j] - v1[j];    
		d2[j] = v3[j] - v1[j];    
    }
	normcrossprod(d1, d2, norm);
	for (int j = 0; j < 3; j++)
		out[j] = norm[j];
}

template<class Drawer>
void TMesh<Drawer>::addnormal(float *n, float *newnorm) 
{ 
	GLfloat norm[3];
	for(int j = 0; j < 3; j++)
		norm[j] = n[j] + newnorm[j];    
	normalize(norm);
	for(int j = 0; j < 3; j++)
		n[j] = norm[j];
}

template<class Drawer>
void TMesh<Drawer>::normalize(float v[3])
{
	GLfloat d = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
	if (d == 0.0)  
		return; 
	v[0] /= d; v[1] /= d; v[2] /= d; 
} 
